/* 算法实现模板 */
import AlgorithmBase from "./AlgorithmBase";
import { mdRender} from "../../../common/Common";
import { toVector,toString} from "../../../common/Vector";
import { DeNode} from "../../../common/DecisionTreeFuncs";
import { drawDecisionTree } from "../../../visual/Tree2D";
import { NodeRange, calcNodeRange } from "../../../common/KDTreeFuncs";
import { showMathStringInTable } from "../../../visual/Table2D";
import Mat from "../../../common/TMatrix";
import 'katex/dist/katex.min.css';


class DecisionTreeC45 extends AlgorithmBase{
  constructor(){
    super('DecisionTreeC45');
    this.descriptionUrl = `${this.homepage}/data/slm/ch5/5.4/READEME5.3.md`;
    this.datasetUrl     = `${this.homepage}/data/slm/ch5/5.4/dataset5.3.json`;
    this.drawtree=[];
    this.step=1;
  }

  init(){ 
    this.reset();
    this.initTrainDS();
  }

  reset(){
    this.dataset = undefined;
    this.trainDS = undefined;
    this.model = {node:{}};
    this.clear();
    this.initDescription();
    this.updateModel();
  }

  initDescription(){
    fetch(this.descriptionUrl).then((response)=>{
      return response.text();
    }).then((txt)=>{

      this.reandMD(txt, this.descriptionDiv)
    })
  }
  
  initTrainDS(){
    this.prepare(this.datasetUrl);
  }

  // 准备数据训练数据
  prepare(url){
    fetch(url).then((response)=>{
      return response.json();
    }).then((json)=>{
      this.trainDS = this.loadModel(json);
      this.updateModel();
    })
  }

  openFileSelector(){
    this.fileSelector.click();
  }

  // 获取文件路径加载
  handleFileChange(event){
    const files = event.target.files;

    for(let i = 0, il = files.length; i < il; i++){
      let reader = new FileReader();
      reader.onload = (e) => {      
        this.prepare(e.target.result);
        if(i === files.length - 1){
          event.target.value = '';
        }
      };
      reader.readAsDataURL(files[i])
    }
  }


  loadModel(json){

    if(json.TrainDS){
      if(json.TrainDS instanceof Array){
        this.trainDSList = [];
        for(let i = 0, il = json.TrainDS.length; i < il; i++){
          const dat = json.TrainDS[i];
          this.trainDSList.push(dat);
        }
        this.trainDS = this.trainDSList[0];
      }
      else{
        let trainDS = json.TrainDS;
        this.trainDS = trainDS;
      }
    }
    return this.trainDS;
  }



  initInputUI(){
    // 文件选择器
    const fileSelector      = document.createElement('input');
    fileSelector.type       = 'file';
    fileSelector.hidden     = true;
    fileSelector.onchange   = this.handleFileChange.bind(this);   
    
    this.addButton(this.domElementList.input, "获取服务器数据", this.initTrainDS.bind(this));
    this.addButton(this.domElementList.input, "使用本地数据", this.openFileSelector.bind(this));
    this.domElementList.input.appendChild(fileSelector);
    this.addButton(this.domElementList.input, "训练", this.train.bind(this));

    this.labelTrainTips = this.addLabel(this.domElementList.input, '');
    this.inputDiv       = this.addDiv(this.domElementList.input);
    this.fileSelector  = fileSelector;
  }

  // 输出UI
  initOutputUI(){
    this.outputDiv   = this.addDiv(this.domElementList.output);
    this.trainLogDiv = this.addDiv(this.domElementList.output);
    
  }

  // 渲染模型到页面
  updateModel(data, label){

    // 显示数据集
    if(this.trainDS){
      if(data === undefined) data = this.trainDS.dataSet;
      if(label === undefined) label = this.trainDS.label;
      var table_data = [];

      var data_label_ = ["ID"];
      for(var i=0;i<label.length;i++){
        data_label_.push("\\textbf{"+label[i]+"}")
      }

      table_data.push(data_label_);

      for(var j=0;j<data.length;j++){
        var data_=[]
        data_.push((j+1).toString())
        for(var k=0;k<data[j].length;k++){
          data_.push("\\textbf{"+data[j][k]+"}")
        }
        table_data.push(data_)
      }
      if(this.inputDiv) this.inputDiv.innerHTML = '';
      showMathStringInTable(this.inputDiv,table_data);
      
    }

  }



  getWordCnt(arr){    //统计重复文字的个数 输出字典进行分类
    var obj = {}; 
    for(var i= 0, l = arr.length; i< l; i++){ 
        var item=arr[i];
        obj[item] = (obj[item] +1 ) || 1; 
    } 
  
    return obj; 
  }

  
  set_fun(matrix){   //返回无重复元素的列表
    var set_matrix = [];
    for(var i=0,il=matrix[0].length;i<il;i++){
      if(set_matrix.indexOf(matrix[0][i])===-1){
        set_matrix.push(matrix[0][i]);
      }
    }
    return set_matrix;
  }
  
    entropy(data){    //信息熵
      let y = [];
      for(let i=0,il=data.length;i<il;i++){
        y.push(data[i][data[0].length-1])
        // console.log(y);
      }
      var obj=this.getWordCnt(y);
      var count=Object.values(obj);
      var n=data.length
      let entr=0;
      for(var i=0; i<count.length; i++)
      {
         var a=-(count[i]/n)*(Math.log2(count[i]/n));
         entr=entr+a;
      }
      return entr;
    }
  
  
    cond_entropy(data,y,cond){   //条件熵
      var cond_x=[];
      var N=y.length;
      for(var i=0;i<data.length;i++){
           cond_x.push(data[i][cond])
        }
  
      var tmp_entro = [];
      var set_cond_x = [];
      for(let i=0,il=cond_x.length;i<il;i++){
        if(set_cond_x.indexOf(cond_x[i])===-1){
          set_cond_x.push(cond_x[i])
          }
        }
      
      for(let i=0,il=set_cond_x.length;i<il;i++){
        var tmp_y =[];
        for(let j=0,jl=cond_x.length;j<jl;j++){
           if (cond_x[j]===set_cond_x[i]){
            tmp_y.push(y[j]);
            } 
          }
        tmp_entro.push(tmp_y.length/N*this.entropy(tmp_y));
        }
        var tmp_entro_ = [];
  
        for(let i=0,il=tmp_entro.length;i<il;i++){
         if(tmp_entro[i]!==NaN){
           tmp_entro_.push(tmp_entro[i]);
         }
         if(tmp_entro[i]!==tmp_entro[i]){
           tmp_entro_[i]=0
         }
    
       }
        var sum = 0;
        for(let i =0,il=tmp_entro_.length;i<il;i++){
          sum = parseFloat(sum)+parseFloat(tmp_entro_[i]);
        }
        return sum;
    
      }
  

    info_gain(data,y,cond){ //信息增益
      return this.entropy(y)-this.cond_entropy(data,y,cond);
    }
   
    info_gain_ratio(data,y,cond){  //信息增益比
      return (this.entropy(y) - this.cond_entropy(data,y,cond))/this.cond_entropy(data,y,cond);
    }


    getdatat(dataset){
        var datat= Array.from(Array(dataset[0].length-1), () => new Array(dataset.length));
        var linemax = dataset.length //15
        var rowmax  = dataset[0].length-1 // 4
        for(let row = 0;row<rowmax;row++){//4
          for(let line = 0;line<linemax;line++){//15
            datat[row][line] = dataset[line][row]
          }
        }
      return datat;
    }
  

    dealdata(dataset,label){
      var data = this.getdatat(dataset);
      console.log(data)
      var hash = this.set_fun(data);
      console.log(hash);
      var flag =0
      var tnum =0
      var fnum =0
      var snum =0
      var tlist =[]
      var flist =[]
      for(var i = 0; i < hash.length; i++) {
        for(var a = 0; a < dataset.length; a++) {
          if(dataset[a][0]==hash[i]&&dataset[a].slice(-1)=="同意"){
            tnum++
            tlist.push(a)
          } 
          if(dataset[a][0]==hash[i]&&dataset[a].slice(-1)=="拒绝"){
            fnum++
            flist.push(a)
          } 
          if(dataset[a][0]==hash[i]){
            snum++
          }
            
          }
        if(tnum==snum){//全是左支  
          console.log('全是左支');
          flag = 1
          
          for(var a = 0; a < tlist.length; a++) {
            if(a!=0)
            tlist[a]=tlist[a]-a
            dataset.splice(tlist[a],1)
            
          }
        }
        if(fnum==snum){//全是右支  
          console.log('全是右支');
          flag += 2
          
          for(var a = 0; a < flist.length; a++) {
            if(a!=0)
            flist[a]=flist[a]-a
  
            dataset.splice(flist[a],1)
          }
          // return dataset
        }
        tnum =0
        fnum =0
        snum =0
        tlist =[]
        flist =[]
        
    }
    var split =0
    switch(flag){
      case 0:
        split =0
        break
      case 1:
        split =1
        break
      case 3:
        split =2
        break    
      }
    return new datasetandsplit(dataset,split)
    }


    sortData(data,ht,label){
      var dataSet = data;
      var dataLabel = label;
      var temp = 0
      // var datat = this.datat
      var datat =this.getdatat(dataSet);

      for(let i = 0 ; i < ht.length-1; i++){
        for(let k = i+1 ; k < ht.length; k++){
          if(ht[k]>ht[i]){
            temp = ht[k]
            ht[k] = ht[i]
            ht[i] = temp
            temp = dataLabel[k]
            dataLabel[k] = dataLabel[i]
            dataLabel[i] = temp
  
            for(let a = 0 ;a<dataSet.length;a++){
              
                temp = dataSet[a][k]
                dataSet[a][k] = dataSet[a][i]
                dataSet[a][i] = temp
            }
            for(let a = 0 ;a<datat[0].length;a++){
              
              temp = datat[k][a]
              datat[k][a] = datat[i][a]
              datat[i][a] = temp
          }
          }
        }
      }
      var hash = this.set_fun(datat);
      return new DataOb(dataSet,dataLabel,hash);
      
    }


    createNewTree(root,dataSet,label){
      var entropyList =[];
      for(let i = 0;i<dataSet[0].length-1;i++){
        entropyList.push(this.info_gain_ratio(dataSet,dataSet,i))
        console.log(entropyList)
        if(this.info_gain_ratio(dataSet,dataSet,i)!=0){ 
          var dl = new drawlist(label[i],this.info_gain_ratio(dataSet,dataSet,i),this.step)
          this.drawtree.push(dl);
        }
      }
  
      this.step++;
      var dataob = this.sortData(dataSet,entropyList,label)
      console.log(dataob.dataSet);
      console.log(dataob.label);
      var datasetandsplit = this.dealdata(dataob.dataSet,dataob.label)
      
      root.name = dataob.label[0];
      root.featureList =dataob.hash;
      root.split = datasetandsplit.split;
      if(datasetandsplit.dataset.length==0)
        {
        console.log('分类结束');
        return new treeElement(dataob.hash,dataob.hash,dataob.label[0],root,null,0)
        }
      else{
        root.children = new treeElement()
        root.children = this.createNewTree(root.children,dataSet,label)
        return root;
      }
    
    }



    outputMap(tree){
      console.log(tree);
      var map = {};
      map['node'] = tree.name
      switch(tree.split){
        case 0:
          map['有'] = {node:'同意'}
          map['没有'] = {node:'拒绝'}
          return map
          break;
        case 1:
          map['有'] = {node:'同意'}
          map['没有'] = this.outputMap(tree.children)
          return map
          break;
        case 2:
          map['有'] = this.outputMap(tree.children)
          map['没有'] = {node:'拒绝'}
          return map
          break;
        case 3:
          map['有'] = this.outputMap(tree.children)
          map['没有'] = this.outputMap(tree.children)
          return map
          break;
      }
    
    }
  
    displayModel(){

      if(this.trainDS){
        var dataSetr =this.trainDS.dataSet;
        var labelr =this.trainDS.label
        var dataSet =  Array.from(Array(dataSetr.length), () => new Array(dataSetr[0].length));
        var label =  Array(labelr.length)
        for(let a=0;a<dataSet.length;a++){
          for(let b =0 ;b<dataSet[a].length;b++){
            dataSet[a][b] = dataSetr[a][b]
          }
        }
        for(let a= 0;a<label.length;a++)
          label[a] = labelr[a] 
        var root = new treeElement() 
        let node = this.model.node;
        
        if(this.figure){
          let range = new NodeRange([[this.figure.xMin, this.figure.xMax + 1],[this.figure.yMin,this.figure.yMax + 1]]);
          calcNodeRange(node, range);
          this.figure.update();
        }
      }
      if(this.outputDiv) this.outputDiv.innerHTML = '';
      drawDecisionTree(this.outputDiv, this.outputMap(this.createNewTree(root,dataSet,label)));
    }



  
  train(){

    this.displayModel();
    let trainLog = [];
    trainLog.push([
      String.raw`\textbf{步骤}`, 
      String.raw`\textbf{特征}`, 
      String.raw`\textbf{信息增益}`,]);
    for(let a = 0 ;a<this.drawtree.length;a++){
      trainLog.push([`${this.drawtree[a].step}`,`${this.drawtree[a].name}`,`${this.drawtree[a].entropy.toFixed(4)}`])
      if(a!=(this.drawtree.length-1)&&this.drawtree[a].step!=this.drawtree[a+1].step){
      trainLog.push(['----','----','----']);
      }
    }
    this.drawtree=[];
    this.step=1;

    if(this.trainLogDiv) this.trainLogDiv.innerHTML = '';
    showMathStringInTable(this.trainLogDiv, trainLog);
   
  }
  
  }


class treeElement {
  constructor(featureList,lastFeatureList,name,parrent,children,split){
    this.name = name
    this.featureList = featureList
    this.lastFeatureList = lastFeatureList;
    this.split = split
  }
  setparrent(parrent){
    this.parrent = parrent
  }
  setchildren(children){
    this.children = children
  }
}



//用于控制下次循环的分支
//目前用flag控制split 0 1 2
class datasetandsplit{
  constructor(dataset,split){
    this.dataset = dataset
    this.split = split
  }
}


class DataOb{
  constructor(dataSet,label,hash){
    this.dataSet =dataSet
    this.label = label
    this.hash = hash
  }
}

class drawlist{
  constructor(name,entropy,step){
    this.name = name
    this.entropy = entropy
    this.step = step
  }
}


export default DecisionTreeC45;